(() => {
  // ------ js进阶第4天 递归实现深copy 加上类型;防抖节流函数的实现加上类型 ------
  /**
   * 案例1：递归实现深copy
   * 1. 泛型<T>代替any，提高类型安全性
   */
  function deepCopy<T>(obj: T): T {
    if (typeof obj !== 'object' || obj === null) {
      return obj;
    }
    let result: any = Array.isArray(obj) ? [] : {};
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        result[key] = deepCopy(obj[key]);
      }
    }
    return result as T;
  }

  type Obj = {
    [index: string]: any
  }
  let obj1: Obj = {
    a: 1,
    b: [1, 2, 3]
  };
  let obj2: any = deepCopy(obj1);
  obj2.b[0] = 99;
  console.log(obj1.b); // 输出 [1, 2, 3]，说明原对象没有受到影响

  /**
   * 案例2 ---防抖版本1：停止触发事件n毫秒后执行回调函数
   * 1. 解决this类型定义问题：使用泛型<T>定义this
   */
  function debounce<T>(func: Function, delay: number) {
    let timer: NodeJS.Timeout;  // 定时器

    return function (this: T, ...args: any[]): void {
      const context = this;  // 记录 this 值,防止在回调函数中丢失
      // let args = arguments;  // 函数参数

      //如果定时器存在，则清除定时器(如果没有,也没必要进行处理)
      timer ? clearTimeout(timer) : null;

      timer = setTimeout(() => {
        // 防止 this 值变为 window
        func.apply(context, args)
      }, delay);
    }
  }
  document.body.onclick = debounce(function () { console.log('hello') }, 1000)

  /**
   * 案例3 ---防抖版本2：触发事件后立即执行回调函数，但是触发后n毫秒内不会再执行回调函数，如果 n 毫秒内触发了事件，也会重新计时。
   */
  function _debounce<T>(func: Function, delay: number) {
    let timer: NodeJS.Timeout | null;  // 定时器

    return function (this: T, ...args: any) {
      let context = this;  // 记录 this 值,防止在回调函数中丢失
      // let args = arguments;  // 函数参数

      // 标识是否立即执行
      let isImmediately = !timer;

      //如果定时器存在，则清除定时器(如果没有,也没必要进行处理)
      timer ? clearTimeout(timer) : null;

      timer = setTimeout(() => {
        timer = null;
      }, delay);

      // isImmediately 为 true 则 执行函数(即首次触发事件)
      isImmediately ? func.apply(context, args) : null;
    }
  }
  document.body.onclick = debounce(function () { console.log('hello') }, 1000)

  /**
   * 案例4 ---节流版本1：使用定时器
   */
  const throttle = <T>(func:Function, limit: number) => {
    let inThrottle: boolean;  // 是否处于节流限制时间内

    return function (this: T,...args: any[]):void {
      const context = this;
      // const args = arguments;

      // 跳出时间限制
      if (!inThrottle) {
        func.apply(context, args);  // 执行回调
        inThrottle = true;
        // 开启定时器计时
        setTimeout(() => inThrottle = false, limit);
      }
    }
  }

  /**
   * 案例5 ---节流版本2: 计算当前时间与上次执行函数时间的间隔
   */
  function throttle1<T>(func:Function, limit: number) { 
    //上次执行时间 
    let previous = 0; 
    return function(this: T,...args: any[]):void { 
        //当前时间 
        let now = Date.now(); 

        let context = this; 
        // let args = arguments; 
        
        // 若当前时间-上次执行时间大于时间限制
        if (now - previous > limit) { 
            func.apply(context, args); 
            previous = now; 
        } 
    } 
} 
})()